package com.sweet.bean;

import com.sweet.util.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Bean转换工具
 *
 * @author limi
 * @version 2.0
 * @company yoozu
 * @date 2022-10-21 21:34
 */
@Slf4j
public final class BeanUtil {
    /**
     * 属性拷贝
     * 实测：属性名一致，类型不一致也会拷贝，如果是基础类型会直接转换，如果是集合则等使用时才爆出泛型异常
     *
     * @param source 源数据
     * @param target 目标类型
     * @param <T>
     * @return
     */
    public static <T> void copy(T source, T target) {
        //空检查
        Assert.notNull(source, "source must not be null");
        Assert.notNull(target, "target must not be null");

        //属性拷贝
        try {
            //Spring BeanUtils类型不同则跳过拷贝，如int到string不转，List<Integer>到List<String>不转，保持可控
            org.springframework.beans.BeanUtils.copyProperties(source, target);

            //Apache BeanUtils2个缺点：基础类型int->string互转成功，List<Integer>转List<String>互转通过，但调用时异常，引发不可控
            //BeanUtils.copyProperties(target, source); apache beanutil对int转string会自动支持，而spring beanutil则不支持
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 属性拷贝
     *
     * @param source           源数据
     * @param target           目标类型
     * @param ignoreProperties 忽略的属性列表
     * @param <T>
     * @return
     */
    public static <T> void copy(T source, T target, String... ignoreProperties) {
        //空检查
        Assert.notNull(source, "source must not be null");
        Assert.notNull(target, "target must not be null");

        //属性拷贝
        org.springframework.beans.BeanUtils.copyProperties(source, target, ignoreProperties);
    }

    /**
     * 将Map转为指定的Bean
     *
     * @param source 数据源
     * @param target 目标类型
     * @return
     */
    public static <T> T map2Bean(Map source, Class<T> target) {
        //空检查
        if (source == null) {
            return null;
        }

        //转换
        try {
            T beam = target.newInstance();
            BeanUtils.populate(beam, source);
            return beam;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将map转为指定的bean
     *
     * @param sources 数据源
     * @param target  目标类型
     * @return
     */
    public static <T> List<T> map2Beans(Collection<Map> sources, Class<T> target) {
        //空检查
        if (sources == null) {
            return null;
        }

        //定义列表
        List<T> result = new ArrayList(sources.size());
        for (Map obj : sources) {
            result.add(map2Bean(obj, target));
        }

        return result;
    }

    /**
     * 属性拷贝，生成新对象
     * 实测：属性名一致，类型不一致也会拷贝，如果是基础类型会直接转换，如果是集合则等使用时才爆出泛型异常
     *
     * @param sources 源数据
     * @param target  目标类型
     * @return
     */
    public static <T> List<T> converts(Collection<?> sources, Class<T> target) {
        //空检查
        if (sources == null) {
            return null;
        }

        //定义列表
        List<T> beans = new ArrayList(sources.size());
        for (Object source : sources) {
            beans.add(convert(source, target));
        }

        return beans;
    }

    /**
     * 属性拷贝，生成新对象
     * 实测：属性名一致，类型不一致也会拷贝，如果是基础类型会直接转换，如果是集合则等使用时才爆出泛型异常
     *
     * @param source 源数据
     * @param target 目标类型
     * @param <T>
     * @return
     */
    public static <T> T convert(Object source, Class<T> target) {
        //空检查
        if (source == null) {
            return null;
        }

        //转换
        T bean;
        if (source.getClass() == target) {
            bean = (T) source;
        } else if (source instanceof Map) {
            bean = map2Bean((Map) source, target);
        } else {
            //生成新对象
            try {
                bean = target.newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            //属性拷贝
            copy(source, bean);
        }

        return bean;
    }
}
